package com.chsi.onjava8.lambda;

import java.util.ArrayList;
import java.util.List;
import java.util.function.IntSupplier;
import java.util.function.Supplier;

/**
 * 如果i是Closure1中的属性，每次调用 getAsInt() 都会增加 i，表明存储是共享的。
 */
class Closure1 {
    int i;
    IntSupplier makeFun(int x) {
        return () -> x + i++;
    }
}

/**
 * 如果 i 是 makeFun() 的局部变量， 在正常情况下，当 makeFun() 完成时 i 就消失。 但它仍可以编译：
 */
class Closure2 {
    IntSupplier makeFun(int x) {
        int i = 0;
        return () -> x + i;
    }
}

/**
 * 由 makeFun() 返回的 IntSupplier “关闭” i 和 x，因此当你调用返回的函数时两者仍然有效。 但请注意，却没有像 Closure1.java 那样递增 i，因为会产生编译时错误。代码示例：
 * 会编译报错
 * x 和 i 的操作都犯了同样的错误：从 Lambda 表达式引用的局部变量必须是 final 或者是等同 final 效果的。
 */
class Closure3 {
    IntSupplier makeFun(int x) {
        int i = 0;
//        注释的才是要说明问题的代码 x++ 和 i++ 都会报错：
//        return () -> x++ + i++;
        return () -> x + i; // 为了不让程序报错 我先改成这样
    }
}

/**
 * 如果使用 final 修饰 x和 i，就不能再递增它们的值了。
 */
class Closure4 {
    IntSupplier makeFun(final int x) {
        final int i = 0;
        return () -> x + i;
    }
}

/**
 * 编译错误
 */
class Closure5 {
    IntSupplier makeFun(int x) {
        int i = 0;
        i++;
        x++;
//      注释的才是要说明问题的代码
//        return () -> x + i;
        int finalI = i;
        int finalX = x;
        return () -> finalX + finalI;
    }
}

/**
 * 解决Closure5返回错误的方式
 */
class Closure6 {
    IntSupplier makeFun(int x) {
        int i = 0;
        i++;
        x++;
        // 这里的final是多余的 因为变量在赋值后没有发生过变化
        final int iFinal = i;
        final int xFinal = x;
        return () -> xFinal + iFinal;
    }
}

/**
 * 验证当把变量改成引用的话，发现编译就会报错
 */
class Closure7 {
    IntSupplier makeFun(int x) {
        Integer i = 0;
        i = i + 1;
//        注释是想要说明的问题
//        return () -> x + i;
        Integer finalI = i;
        return () -> x + finalI;
    }
}

/**
 * 测试比较特殊的集合类,发现集合类是可以修改其中的元素的
 */
class Closure8 {
    Supplier<List<Integer>> makeFun() {
        final List<Integer> ai = new ArrayList<>();
        ai.add(1);
        return () -> ai;
    }
}

class AnonymousClosure {
    IntSupplier makeFun(int x) {
        int i = 0;
        // 同样规则的应用:
//         i++; // 非等同 final 效果
//         x++; // 非等同 final 效果
        return new IntSupplier() {
            public int getAsInt() { return x + i; }
        };
    }
}

public class SharedStorage {
    public static void main(String[] args) {
        System.out.println("------------------Closure1------------------");
        Closure1 c1 = new Closure1();
        Closure1 c1_2 = new Closure1();
        IntSupplier f1 = c1.makeFun(0);
        IntSupplier f2 = c1.makeFun(0);
        IntSupplier f3 = c1.makeFun(0);
        IntSupplier f1_2 = c1_2.makeFun(0);
        System.out.println(f1);
        System.out.println(f1.getAsInt());
        System.out.println(f2);
        System.out.println(f2.getAsInt());
        System.out.println(f3);
        System.out.println(f3.getAsInt());
        System.out.println(f1_2);
        System.out.println(f1_2.getAsInt());
        System.out.println("------------------Closure2------------------");
        System.out.println("对比Closure4会发现，lambda表达式使用的变量应是final或者等同final效果的");
        Closure2 c2 = new Closure2();
        IntSupplier f2_1 = c2.makeFun(0);
        IntSupplier f2_2 = c2.makeFun(0);
        IntSupplier f2_3 = c2.makeFun(0);
        System.out.println(f2_1);
        System.out.println(f2_1.getAsInt());
        System.out.println(f2_2);
        System.out.println(f2_2.getAsInt());
        System.out.println(f2_3);
        System.out.println(f2_3.getAsInt());
        System.out.println("------------------Closure3------------------");
        System.out.println("Closure3由于使用非final变量会编译报错");
        System.out.println("------------------Closure4------------------");
        Closure4 c4 = new Closure4();
        IntSupplier f4_1 = c4.makeFun(0);
        IntSupplier f4_2 = c4.makeFun(0);
        IntSupplier f4_3 = c4.makeFun(0);
        System.out.println(f4_1);
        System.out.println(f4_1.getAsInt());
        System.out.println(f4_2);
        System.out.println(f4_2.getAsInt());
        System.out.println(f4_3);
        System.out.println(f4_3.getAsInt());
        System.out.println("------------------Closure5------------------");
        System.out.println("Closure5会编译报错就不用调用了");
        System.out.println("------------------Closure6------------------");
        Closure6 c6 = new Closure6();
        IntSupplier f6_1 = c6.makeFun(0);
        IntSupplier f6_2 = c6.makeFun(0);
        IntSupplier f6_3 = c6.makeFun(0);
        System.out.println(f6_1);
        System.out.println(f6_1.getAsInt());
        System.out.println(f6_2);
        System.out.println(f6_2.getAsInt());
        System.out.println(f6_3);
        System.out.println(f6_3.getAsInt());
        System.out.println("------------------Closure7------------------");
        System.out.println("Closure7会编译报错就不用调用了");
        System.out.println("------------------Closure8------------------");
        Closure8 c8 = new Closure8();
        List<Integer> l1 = c8.makeFun().get();
        List<Integer> l2 = c8.makeFun().get();
        System.out.println(l1);
        System.out.println(l2);
        l1.add(42);
        l2.add(96);
        System.out.println(l1);
        System.out.println(l2);
    }
}